package wechat

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"crypto/md5"
	"crypto/sha1"
	"encoding/base64"
	"encoding/hex"
	"encoding/xml"
	"fmt"
	"io/ioutil"
	"math/rand"
	"net/http"
	"reflect"
	"sort"
	"strings"
	"time"
)

// sha1s 加密
func sha1s(s string) string {
	r := sha1.Sum([]byte(s))
	return hex.EncodeToString(r[:])
}

// wxpayCalcSign oauth签名加密
func wxpayCalcSign(mReq map[string]interface{}, key string) string {
	sortedKeys := make([]string, 0)
	for k := range mReq {
		sortedKeys = append(sortedKeys, k)
	}
	sort.Strings(sortedKeys)
	var signStrings string
	for _, k := range sortedKeys {
		if k == "sign" {
			continue
		}
		value := fmt.Sprint(mReq[k])
		if value != "" && value != "0" {
			signStrings += k + "=" + value + "&"
		}
	}
	if key != "" {
		signStrings = signStrings + "key=" + key
	}
	md5Ctx := md5.New()
	md5Ctx.Write([]byte(signStrings))
	cipherStr := md5Ctx.Sum(nil)
	upperSign := strings.ToUpper(hex.EncodeToString(cipherStr))
	return upperSign
}

//wXBizDataDecrypt 解密
func wXBizDataDecrypt(sessionKey, encryptedData, iv string) (decrypted []byte, err error) {
	var (
		decryptedkey, decryptedIV, decryptedData []byte
	)
	decryptedkey, err = base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return
	}
	decryptedData, err = base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return
	}
	decryptedIV, err = base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return
	}
	block, err := aes.NewCipher(decryptedkey)
	if err != nil {
		return
	}
	blockMode := cipher.NewCBCDecrypter(block, decryptedIV)
	decrypted = make([]byte, len(decryptedData))
	// origData := crypted
	blockMode.CryptBlocks(decrypted, decryptedData)
	decrypted = pKCS5UnPadding(decrypted)
	return
}

//wXBizDataSignature 解签名
func wXBizDataSignature(sessionKey, rawData string) string {
	hash := sha1.New()
	_, err := hash.Write([]byte(rawData + sessionKey))
	if err != nil {
		return ""
	}
	sign := hash.Sum(nil)
	return string(sign)
}

//pKCS5UnPadding PKCS5
func pKCS5UnPadding(origData []byte) []byte {
	length := len(origData)
	// 去掉最后一个字节 unpadding 次
	unpadding := int(origData[length-1])
	return origData[:(length - unpadding)]
}

//Request 发起请求
func request(reqxml interface{}, requrl string) (respBytes []byte, err error) {
	bytesreq, err := xml.Marshal(reqxml)
	if err != nil {
		fmt.Println("转换XML出错:", err)
		return
	}
	strreq := strings.Replace(string(bytesreq), "UnifiedOrderReq", "xml", -1)
	bytesreq = []byte(strreq)
	req, err := http.NewRequest("POST", requrl, bytes.NewReader(bytesreq))
	if err != nil {
		fmt.Println("New Http Request发生错误，原因:", err)
		return
	}
	req.Header.Set("Accept", "application/xml")
	req.Header.Set("Content-Type", "application/xml;charset=utf-8")
	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		fmt.Println("接口发送错误, 原因:", err)
		return
	}
	respBytes, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		fmt.Println("解析返回body错误", err)
		return
	}
	return
}

//RandomStr 生成随机字符串
func randomStr(length int) string {
	str := "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
	bs := []byte(str)
	var result []byte
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	for i := 0; i < length; i++ {
		result = append(result, bs[r.Intn(len(bs))])
	}
	return string(result)
}

//structToMap 结构体转map
func structToMap(st interface{}) (m map[string]interface{}) {
	m = make(map[string]interface{})
	t := reflect.TypeOf(st)
	te := t.Elem()
	tlength := te.NumField()
	values := reflect.ValueOf(st).Elem()
	for i := 0; i < tlength; i++ {
		field := te.Field(i)
		key := field.Tag.Get("xml")
		val := values.FieldByName(field.Name)
		kind := val.Kind().String()
		switch kind {
		case "string":
			m[key] = val.String()
		case "int", "int64":
			m[key] = val.Int()
		case "float", "float64":
			m[key] = val.Float()
		}
	}
	return
}

//VerifySign 验证签名
func verifySign(needVerifyM map[string]interface{}, sign, secret string) bool {
	signCalca := wxpayCalcSign(needVerifyM, secret)
	if sign == signCalca {
		return true
	}
	return false
}
